package com.tz.tools;

import com.google.common.base.CaseFormat;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * Object工具类
 *
 * @author zjc
 * @since 2023/1/5 9:07
 */
public class ObjectTools {

    private static final String getMethodPrefix = "get";
    private static final String isMethodPrefix = "is";

    /**
     * 获取对象
     *
     * @param object   对象
     * @param supplier 函数
     * @param <T>      泛型
     * @return 结果
     */
    public static <T> T defaultIfNull(T object, Supplier<T> supplier) {
        return Objects.nonNull(object) ? object : supplier.get();
    }

    /**
     * 提取对象属性(对象为null,则返回null)
     *
     * @param object   对象
     * @param function 提取函数
     * @return 属性值
     */
    public static <R, T> T getProperty(R object, Function<R, T> function) {
        return getProperty(object, function, null);
    }

    /**
     * 提取对象属性(对象为null，则返回默认值)
     *
     * @param object       对象
     * @param function     提取函数
     * @param defaultValue 默认值
     * @return 属性值
     */
    public static <R, T> T getProperty(R object, Function<R, T> function, T defaultValue) {
        Objects.requireNonNull(function, "function函数不能为空");
        return Objects.isNull(object) ? defaultValue : function.apply(object);
    }

    /**
     * 根据匿名函数获取bean属性名称
     */
    public static <T> String extractFieldName(SFunction<T, ?> sFunction, CaseFormat caseFormat) {
        Objects.requireNonNull(sFunction, "匿名函数不能为空");
        try {
            Method declaredMethod = sFunction.getClass().getDeclaredMethod("writeReplace");
            declaredMethod.setAccessible(Boolean.TRUE);
            SerializedLambda serializedLambda = (SerializedLambda) declaredMethod.invoke(sFunction);
            String method = serializedLambda.getImplMethodName();

            String attr;
            if (method.startsWith(getMethodPrefix)) {
                attr = (char) (method.charAt(3) + 32) + method.substring(4);
            } else if (method.startsWith(isMethodPrefix)) {
                attr = (char) (method.charAt(2) + 32) + method.substring(3);
            } else {
                throw new IllegalStateException("无法处理的方法名" + method);
            }
            return CaseFormat.UPPER_CAMEL.to(caseFormat, attr);
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 支持序列化的 Function
     *
     * @author zjc
     * @since 2022/12/3 13:20
     */
    @FunctionalInterface
    public interface SFunction<T, R> extends Function<T, R>, Serializable {
    }

    /**
     * 判断指定对象是否所有属性皆为 null 或 empty
     *
     * @param obj 目标对象
     * @return 是/否
     */
    public static boolean allFieldIsEmpty(Object obj) {
        if (Objects.isNull(obj)) {
            return true;
        }
        List<Field> fields = getAllField(obj.getClass());
        boolean allIsEmpty = true;
        for (Field field : fields) {
            // 设置允许通过反射访问私有变量
            field.setAccessible(true);

            String fieldName = field.getName();
            // 过滤序列化支持字段
            if (Objects.equals(fieldName, "serialVersionUID")) {
                continue;
            }
            //过滤jacoco编译期间加入的 JacocoData 字段
            if (field.isSynthetic()) {
                continue;
            }

            Object fieldValue;
            try {
                fieldValue = field.get(obj);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(String.format("遍历获取指定对象属性值异常, error:%s", e));
            }

            if (Objects.nonNull(fieldValue)) {
                if (Collection.class.isAssignableFrom(fieldValue.getClass())) {
                    if (CollectionUtils.isNotEmpty((Collection<?>) fieldValue)) {
                        allIsEmpty = false;
                        break;
                    }
                } else if (Map.class.isAssignableFrom(fieldValue.getClass())) {
                    if (MapUtils.isNotEmpty((Map<?, ?>) fieldValue)) {
                        allIsEmpty = false;
                        break;
                    }
                } else if (fieldValue instanceof String) {
                    if (StringUtils.isNotEmpty((String) fieldValue)) {
                        allIsEmpty = false;
                        break;
                    }
                } else {
                    allIsEmpty = false;
                    break;
                }
            }
        }
        return allIsEmpty;
    }

    /**
     * 获取指定类的所有属性, 包括父类属性
     *
     * @param cl 指定类
     * @return 属性集合
     */
    public static List<Field> getAllField(Class<?> cl) {
        List<Field> fields = new ArrayList<>();
        if (Objects.nonNull(cl.getSuperclass())) {
            fields.addAll(getAllField(cl.getSuperclass()));
        }
        Field[] declaredFields = cl.getDeclaredFields();
        fields.addAll(Arrays.asList(declaredFields));
        return fields;
    }

    /**
     * 用对象1的值覆盖对象2的
     *
     * @param source     对象1
     * @param target     对象2
     * @param onlyNoNull 是否只用o1中非空的属性覆盖
     * @return 对象2
     */
    public static Object overrideField(Object source, Object target, Boolean onlyNoNull) {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");
        Class<?> actualEditable = target.getClass();
        PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(actualEditable);
        for (PropertyDescriptor targetPd : targetPds) {
            if (targetPd.getWriteMethod() != null) {
                PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null && sourcePd.getReadMethod() != null) {
                    try {
                        Method readMethod = sourcePd.getReadMethod();
                        if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                            readMethod.setAccessible(true);
                        }
                        Object value = readMethod.invoke(source);
                        // 这里判断以下value是否为空 当然这里也能进行一些特殊要求的处理 例如绑定时格式转换等等
                        if (onlyNoNull && value == null) {
                            continue;
                        }
                        Method writeMethod = targetPd.getWriteMethod();
                        if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                            writeMethod.setAccessible(true);
                        }
                        writeMethod.invoke(target, value);
                    } catch (Throwable ex) {
                        throw new FatalBeanException("Could not copy properties from source to target", ex);
                    }
                }
            }
        }
        return target;
    }

    /**
     * 判断对象是否是数组
     *
     * @param obj 输入对象
     * @return 是/否
     */
    public static boolean isArray(Object obj) {
        if (obj == null) {
            return false;
        }
        return obj.getClass().isArray();
    }
}
